# ==== Requirements ====
#
# For a given scenario specified by the caller, execute the scenario
# and verify the following requirements:
#
# - Replication should not fail.
# - The servers should have the same table data.
# - The servers should write the same to their binary logs, if
#   they use the same setting for binlog_row_image.
# - The servers should have the same binary representations of JSON
#   objects (the actual representation is not accessible through
#   SQL, but we check the sizes using JSON_STORAGE_SIZE).
# - The script also prints decoded rows to the result file so they
#   can be sanity-checked by a human.
#
# ==== Implementation ====
#
# Does the following:
#  1. Execute one or several statements on MASTER
#  2. Replicate to slave_1, slave_2, slave_3, slave_4
#  3. Run mysqlbinlog -v on master. Convert the pseudo-SQL output to
#     real SQL, and apply on 'decoded'. Take the BINLOG statements and
#     apply to 'base64'.
#  4. Run the same mysqlbinlog -v on all servers
#  5. Verify that all mysqlbinlog pseudo-SQL was identical
#  6. Verify that all table data is identical
#  7. Verify that the sizes of JSON objects are identical
#
# ==== Usage ====
#
# --let $master_con= CONNECTION
# --let $slave_1_con= CONNECTION
# --let $slave_2_con= CONNECTION
# --let $slave_3_con= CONNECTION
# --let $slave_4_con= CONNECTION
# --let $slave_5_con= CONNECTION
# --let $base64_con= CONNECTION
# --let $decoded_con= CONNECTION
# [--let $rows= (comma-separated-values)[, (comma-separated-values)[, (...)]]
# [--let $stmt_pre= PREFIX]
# [--let $stmt_post= SUFFIX]
# --let $stmt= STATEMENT
# --let $column_def= DEFINITION
# --let $table= DATABASE.TABLE
# --let $compare_size_columns= COLUMN[, COLUMN[, COLUMN...]]
# [--let $echo_stmt= 1]
# [--let $echo_select= COLUMNS]
# [--let $echo_decoded_rows= 1]
# [--let $first_case= N]
# --source include/check_mysqlbinlog_decoded_rows.inc
#
# Assumptions:
#   Replication should be running from server_1 to server_2.
#   server_3 should be configured but not replicating.
#
# Parameters:
#   $master_con, $slave_[1|2|3|4|5]_con, $base64_con, $decoded_con
#     Connections: statement will originally be executed on
#     $master_con, then replicated to $slave_1_con, $slave_2_con,
#     $slave_3_con, $slave_4_con, and $slave_5_con.
#     mysqlbinlog's base64 output will be applied on $base64_con and
#     the decoded pseudo-SQL will be applied on $decoded_con
#
#   $column_def
#     Whatever should be between the open parenthesis and closing
#     parenthesis in CREATE TABLE (...).  If not given, assumes the
#     invoker has already created the table.
#
#   $stmt
#     The statement(s) to execute.
#
#   $stmt_pre
#     This is prepended to $stmt
#
#   $stmt_post
#     This is appended to $stmt
#
#   $table
#     The qualified name of the table that will be compared on
#     server_1, server_2, and server_3 using include/diff_tables.inc
#
#   $echo_stmt
#     Print the statement before executing it.
#
#   $echo_select
#     If set, issue SELECT $echo_select FROM $table at the beginning
#     and end of the script.  Examples: To select all columns, set
#     $echo_select=*; to select columns 'i' and 'j', set
#     $echo_select=i,j.
#
#   $echo_decoded_rows
#     If set to 1, print the generated SQL statement after decoding it
#     using mysqlbinlog, on master, slave_2, and slave_4 (the output
#     is expected to differ on these, because of the different
#     binlog_row_image).  If set to 2, print it only on master (useful
#     when the full format is too big).
#
#   $compare_size_columns
#     Comma-separated list of names of columns.  The sizes of the listed
#     columns will be compared on all servers.
#
#   $first_case
#     For debugging: If set to a number N>=1, skip all invocations of
#     this script before the Nth.


# Print header
if (!$first_case)
{
  --let $first_case= 1
}
if (!$rpl_apply_decoded_rows_case_counter)
{
  --let $rpl_apply_decoded_rows_case_counter= 0
}
--inc $rpl_apply_decoded_rows_case_counter

if ($rpl_apply_decoded_rows_case_counter >= $first_case)
{

  --echo ---- $rpl_apply_decoded_rows_case_counter. $desc ----

  --disable_query_log

  --let $include_filename= rpl/row_jsondiff_scenario.inc
  --source include/begin_include_file.inc

  # Save binlog positions on all servers
  --let $save_binlog_position_in_sql= 1
  --connection $master_con
  --source include/rpl/save_binlog_file_position.inc
  --connection $slave_1_con
  --source include/rpl/save_binlog_file_position.inc
  --connection $slave_2_con
  --source include/rpl/save_binlog_file_position.inc
  --connection $slave_3_con
  --source include/rpl/save_binlog_file_position.inc
  --connection $slave_4_con
  --source include/rpl/save_binlog_file_position.inc
  --connection $slave_5_con
  --source include/rpl/save_binlog_file_position.inc
  --connection $base64_con
  --source include/rpl/save_binlog_file_position.inc
  --connection $decoded_con
  --source include/rpl/save_binlog_file_position.inc

  if ($column_def)
  {
    if ($column_def != $_last_column_def)
    {
      --echo * CREATE TABLE $table ($column_def)
      --let $rpl_sql= SET SESSION SQL_LOG_BIN = 0; DROP TABLE IF EXISTS $table; CREATE TABLE $table ($column_def); SET SESSION SQL_LOG_BIN = 1;
      --source include/rpl/for_each_server_stmt.inc
    }
  }
  --let $_last_column_def= $column_def

  if ($rows)
  {
    --let $rpl_sql= SET SQL_LOG_BIN = 0; TRUNCATE $table; INSERT INTO $table VALUES $rows; SET SQL_LOG_BIN = 1;
    --source include/rpl/for_each_server_stmt.inc
  }
  if ($echo_select)
  {
    --echo # Before update
    eval SELECT $echo_select FROM $table;
  }

  # Run query on MASTER and sync to slaves
  --connection $master_con
  if ($echo_stmt)
  {
    --echo $stmt_pre $stmt $stmt_post
  }
  eval $stmt_pre $stmt $stmt_post;

  if ($echo_select)
  {
    --echo # After update
    eval SELECT $echo_select FROM $table;
  }

  if ($echo_progress)
  {
    --echo # Syncing to three slaves
  }
  --source include/rpl/sync.inc

  # Prepare for get_mysqlbinlog_decoded_rows
  SET @@SESSION.GROUP_CONCAT_MAX_LEN= 65536;
  --let $columns= `SELECT GROUP_CONCAT(CONCAT(COLUMN_NAME, '/', COLUMN_TYPE) ORDER BY ORDINAL_POSITION SEPARATOR '; ') FROM INFORMATION_SCHEMA.COLUMNS WHERE CONCAT(TABLE_SCHEMA, '.', TABLE_NAME) = '$table'`
  if ($rpl_debug)
  {
    --echo columns=$columns
  }
  --let $mysqlbinlog_positions_from_sql_variables= 1
  --let $mysqlbinlog_options= --force-if-open

  # Get decoded rows on MASTER
  --source include/rpl/get_mysqlbinlog_decoded_rows.inc
  --let $decoded_sql_master= $decoded_sql

  if ($echo_decoded_rows)
  {
    --echo # Decoded rows
    --echo $decoded_sql_master
  }

  # Run converted pseudo-SQL on $decoded_con (unless nothing was logged)
  if ($decoded_sql_master)
  {
    if ($echo_progress)
    {
      --echo # Applying decoded SQL
    }
    --connection $decoded_con
    eval $decoded_sql_master;
  }

  # Run BINLOG base64 on $base64_con
  if ($echo_progress)
  {
    --echo # Applying base64
  }
  --connection $base64_con
  --let $base64_port= `SELECT @@GLOBAL.PORT`
  --exec $MYSQL --user=root --host=127.0.0.1 --port=$base64_port < $output_file
  --remove_file $output_file

  if ($echo_progress)
  {
    --echo # Comparing decoded output
  }

  # Get mysqlbinlog output on all other servers too.

  --connection $slave_1_con
  --source include/rpl/get_mysqlbinlog_decoded_rows.inc
  --let $decoded_sql_slave_1= $decoded_sql
  --remove_file $output_file

  --connection $slave_2_con
  --source include/rpl/get_mysqlbinlog_decoded_rows.inc
  --let $decoded_sql_slave_2= $decoded_sql
  --remove_file $output_file

  --connection $slave_3_con
  --source include/rpl/get_mysqlbinlog_decoded_rows.inc
  --let $decoded_sql_slave_3= $decoded_sql
  --remove_file $output_file

  --connection $slave_4_con
  --source include/rpl/get_mysqlbinlog_decoded_rows.inc
  --let $decoded_sql_slave_4= $decoded_sql
  --remove_file $output_file

  --connection $slave_5_con
  --source include/rpl/get_mysqlbinlog_decoded_rows.inc
  --let $decoded_sql_slave_5= $decoded_sql
  --remove_file $output_file

  --connection $base64_con
  --source include/rpl/get_mysqlbinlog_decoded_rows.inc
  --let $decoded_sql_base64= $decoded_sql
  --remove_file $output_file

  --connection $decoded_con
  --source include/rpl/get_mysqlbinlog_decoded_rows.inc
  --let $decoded_sql_decoded= $decoded_sql
  --remove_file $output_file

  if ($echo_decoded_rows == 1)
  {
    --echo # Decoded rows, full image
    --echo $decoded_sql_slave_2

    --echo # Decoded rows, minimal image when master has full image
    --echo $decoded_sql_slave_4
  }

  # Verify that mysqlbinlog has the same output on all servers
  --let $mismatch=

  # When using no primary key, master will generate a full BI but a
  # minimal AI.  Due to BUG#86554, slave will then generate a full AI,
  # so it will be different from the master.  Hence disable comparison
  # with master in the no-pk case.
  if ($key)
  {
    if ($decoded_sql_slave_1 != $decoded_sql_master)
    {
      --let $mismatch= slave_1 vs master;
    }
  }
  # Since slave_2 and slave_3 use binlog_row_image=FULL, they are
  # expected to have different decoded SQL statements from the master,
  # and will only be compared with each other.
  if ($decoded_sql_slave_3 != $decoded_sql_slave_2)
  {
    --let $mismatch= $mismatch slave_3 vs slave_2;
  }
  # Due to BUG#26258329, don't compare slave_4/slave_5 with master,
  # only with each other.
  #if ($decoded_sql_slave_4 != $decoded_sql_master)
  #{
  #  --let $mismatch= slave_4 vs master;
  #}
  if ($decoded_sql_slave_4 != $decoded_sql_slave_5)
  {
    --let $mismatch= $mismatch slave_4 vs slave_5;
  }
  # Must compare with slave_1 and not master due to BUG#86554
  if ($decoded_sql_base64 != $decoded_sql_slave_1)
  {
    --let $mismatch= $mismatch base64 vs slave_1;
  }
  if ($decoded_sql_decoded != $decoded_sql_master)
  {
    --let $mismatch= $mismatch decoded vs master;
  }
  if ($mismatch)
  {
    --source include/rpl/debug/show_debug_info.inc
    --echo FAILURE: output on master: <<<$decoded_sql_master>>>
    --echo FAILURE: output on slave_1: <<<$decoded_sql_slave_1>>>
    --echo FAILURE: output on slave_2: <<<$decoded_sql_slave_2>>>
    --echo FAILURE: output on slave_3: <<<$decoded_sql_slave_3>>>
    --echo FAILURE: output on slave_4: <<<$decoded_sql_slave_4>>>
    --echo FAILURE: output on slave_5: <<<$decoded_sql_slave_5>>>
    --echo FAILURE: output on base64: <<<$decoded_sql_base64>>>
    --echo FAILURE: output on decoded: <<<$decoded_sql_decoded>>>
    --echo FAILURE: The following decoded rows are different: $mismatch
    --echo FAILURE while executing case #$rpl_apply_decoded_rows_case_counter
    --die Decoded rows look different on the different hosts
  }

  # Verify that tables are the same on all servers
  if ($echo_progress)
  {
    --echo # Comparing tables
  }
  --let $diff_tables= $master_con:$table, $slave_1_con:$table, $slave_2_con:$table, $slave_3_con:$table, $slave_4_con:$table, $slave_5_con:$table, $base64_con:$table, $decoded_con:$table
  --source include/diff_tables.inc

  if ($compare_size_columns)
  {
    if ($echo_progress)
    {
      --echo # Comparing sizes of binary representations
    }
    --let $size_query= `SELECT REPLACE('$compare_size_columns', ', ', '), "+", JSON_STORAGE_SIZE(')`
    --let $size_query= SELECT CONCAT(JSON_STORAGE_SIZE($size_query)) FROM $table

    --connection $master_con
    --let $size_master= `$size_query`
    --connection $slave_1_con
    --let $size_slave_1= `$size_query`
    --connection $slave_2_con
    --let $size_slave_2= `$size_query`
    --connection $slave_3_con
    --let $size_slave_3= `$size_query`
    --connection $slave_4_con
    --let $size_slave_4= `$size_query`
    --connection $slave_5_con
    --let $size_slave_5= `$size_query`
    --connection $base64_con
    --let $size_base64= `$size_query`
    --connection $decoded_con
    --let $size_decoded= `$size_query`

    # Compare binary sizes
    --let $mismatch=
    if ($size_slave_1 != $size_master)
    {
      --let $mismatch= slave_1
    }
    if ($size_slave_2 != $size_master)
    {
      --let $mismatch= $mismatch slave_2
    }
    if ($size_slave_3 != $size_master)
    {
      --let $mismatch= $mismatch slave_3
    }
    if ($size_slave_4 != $size_master)
    {
      --let $mismatch= $mismatch slave_4
    }
    if ($size_slave_5 != $size_master)
    {
      --let $mismatch= $mismatch slave_5
    }
    if ($size_base64 != $size_master)
    {
      --let $mismatch= $mismatch base64
    }
    if ($size_decoded != $size_master)
    {
      --let $mismatch= $mismatch decoded
    }
    if ($mismatch)
    {
      --source include/rpl/debug/show_debug_info.inc
      --echo FAILURE: sizes on master: <<<$size_master>>>
      --echo FAILURE: sizes on slave_1: <<<$size_slave_1>>>
      --echo FAILURE: sizes on slave_2: <<<$size_slave_2>>>
      --echo FAILURE: sizes on slave_3: <<<$size_slave_3>>>
      --echo FAILURE: sizes on slave_4: <<<$size_slave_4>>>
      --echo FAILURE: sizes on slave_5: <<<$size_slave_5>>>
      --echo FAILURE: sizes on base64: <<<$size_base64>>>
      --echo FAILURE: sizes on decoded: <<<$size_decoded>>>
      --echo FAILURE: The following are different from master: $mismatch
      --echo FAILURE while executing case #$rpl_apply_decoded_rows_case_counter
      --die Sizes of binary representations are different on the different hosts
    }
  }

  --let $include_filename= rpl/row_jsondiff_scenario.inc
  --source include/end_include_file.inc
}
